package dd.lo.practice;

import dd.lo.util.ImageUtils;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Slider;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import org.apache.commons.lang3.math.NumberUtils;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.text.DecimalFormat;

public class AffineDisplay extends Application {

    public static void start(String[] args) {
        launch(args);
    }

    private static final int DEFAULT_WIDTH = 1200;
    private static final int DEFAULT_HEIGHT = 900;

    private static final DecimalFormat df = new DecimalFormat("0.##");

    private Mat srcImg;

    private SimpleStringProperty tilt; //倾斜，算水平边的百分比
    private SimpleStringProperty angle; //旋转角度
    private SimpleStringProperty scale; //缩放

    private Slider tiltSlider;
    private Slider angleSlider;
    private Slider scaleSlider;
    private ImageView imageView;

    private void imageProcess() {
        Task<Void> task = new Task<Void>() {
            @Override
            protected Void call() {
                if (null == srcImg || srcImg.empty()) return null;
                Mat displayImg = Mat.zeros(srcImg.cols(), srcImg.rows(), srcImg.type());
                int tiltV = NumberUtils.toInt(tilt.getValue(), 0);
                double tiltP = tiltV / 100.0;
                int angleV = NumberUtils.toInt(angle.getValue(), 0);
                double scaleV = NumberUtils.toDouble(scale.getValue(), 1);
                //旋转缩放
                Point center = new Point(displayImg.cols() / 2.0, displayImg.rows() / 2.0);
                Mat rotateMat = Imgproc.getRotationMatrix2D(center, angleV, scaleV);
                Imgproc.warpAffine(srcImg, displayImg, rotateMat, displayImg.size());
                //仿射变换
                MatOfPoint2f srcTriangle = new MatOfPoint2f(
                        new Point(0, 0),
                        new Point(srcImg.cols() - 1, 0),
                        new Point(0, srcImg.rows() - 1)
                );
                MatOfPoint2f dstTriangle = new MatOfPoint2f(
                        new Point(0, srcImg.rows() * tiltP),
                        new Point(srcImg.cols() - 1 - srcImg.cols() * tiltP, srcImg.rows() * tiltP),
                        new Point(srcImg.cols() * tiltP, srcImg.rows() - 1 - srcImg.rows() * tiltP)
                );
                Mat warpMat = Imgproc.getAffineTransform(srcTriangle, dstTriangle);
                Imgproc.warpAffine(displayImg, displayImg, warpMat, displayImg.size());
                //显示
                ImageUtils.fitImgSize(displayImg, DEFAULT_WIDTH, DEFAULT_HEIGHT);
                MatOfByte buffer = new MatOfByte();
                Imgcodecs.imencode(".jpeg", displayImg, buffer);
                Platform.runLater(() -> imageView.setImage(new Image(new ByteArrayInputStream(buffer.toArray()))));
                return null;
            }
        };
        new Thread(task).start();
    }

    @Override
    public void start(Stage stage) throws Exception {
        Button imgChooserBtn = new Button("选择图片");
        imgChooserBtn.setOnMouseClicked(e -> {
            FileChooser fileChooser = new FileChooser();
            fileChooser.setTitle("选择图片");
            fileChooser.setInitialDirectory(new File(System.getProperty("user.home") + "/Downloads")
            );
            fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("All Images", "*.*"));
            File imgFile = fileChooser.showOpenDialog(stage);
            if (null == imgFile) return;
            srcImg = Imgcodecs.imread(imgFile.getAbsolutePath(), Imgcodecs.IMREAD_UNCHANGED);
            if (srcImg.empty()) System.out.println("图片读取失败");
            imageProcess();
        });
        //倾斜程度
        Text tiltTip = new Text("倾斜程度（%）：");
        tilt = new SimpleStringProperty();
        Text tiltText = new Text();
        tiltText.textProperty().bind(tilt);
        tiltSlider = new Slider(0, 40, 0);
        tiltSlider.setPrefWidth(DEFAULT_WIDTH * 0.3);
        tiltSlider.setShowTickLabels(true);
        tiltSlider.setShowTickMarks(true);
        tiltSlider.setSnapToTicks(true);
        tiltSlider.setMajorTickUnit(5.0);
        tiltSlider.setMinorTickCount(4);
        tiltSlider.setBlockIncrement(1.0);
        tiltSlider.valueProperty().addListener((observable, oldValue, newValue) -> {
            tilt.set(Math.round(newValue.doubleValue()) + "");
            imageProcess();
        });
        tilt.set(Math.round(tiltSlider.getValue()) + ""); //初始化值
        //旋转角度的设定
        Text angleTip = new Text("旋转角度：");
        angle = new SimpleStringProperty();
        Text angleText = new Text();
        angleText.textProperty().bind(angle);
        angleSlider = new Slider(0, 360, 0);
        angleSlider.setPrefWidth(DEFAULT_WIDTH * 0.3);
        angleSlider.setShowTickLabels(true);
        angleSlider.setShowTickMarks(true);
        angleSlider.setSnapToTicks(true);
        angleSlider.setMajorTickUnit(90.0);
        angleSlider.setMinorTickCount(1);
        angleSlider.setBlockIncrement(1.0);
        angleSlider.valueProperty().addListener((observable, oldValue, newValue) -> {
            angle.set(Math.round(newValue.doubleValue()) + "");
            imageProcess();
        });
        angle.set(Math.round(angleSlider.getValue()) + ""); //初始化值
        //缩放比例的设定
        Text scaleTip = new Text("缩放比例：");
        scale = new SimpleStringProperty();
        Text scaleText = new Text();
        scaleText.textProperty().bind(scale);
        scaleSlider = new Slider(0.1, 2, 1);
        scaleSlider.setPrefWidth(DEFAULT_WIDTH * 0.3);
        scaleSlider.setBlockIncrement(0.1);
        scaleSlider.valueProperty().addListener((observable, oldValue, newValue) -> {
            scale.set(df.format(newValue.doubleValue()));
            imageProcess();
        });
        scale.set(scaleSlider.getValue() + ""); //初始化值
        //关闭按钮
        Button exitBtn = new Button("关闭");
        exitBtn.setOnMouseClicked(e -> Platform.exit());
        VBox rows = new VBox(0);
        rows.setPadding(new Insets(10, 10, 0, 10));
        HBox row0 = new HBox(10);
        row0.setPadding(new Insets(5, 0, 5, 0));
        row0.getChildren().addAll(imgChooserBtn, exitBtn);
        HBox row1 = new HBox(10);
        row1.setPadding(new Insets(5, 0, 5, 0));
        row1.getChildren().addAll(tiltTip, tiltSlider, tiltText, angleTip, angleSlider, angleText, scaleTip, scaleSlider, scaleText);
        rows.getChildren().addAll(row0, row1);
        BorderPane root = new BorderPane();
        imageView = new ImageView(); //imageView放到一个pane的中间
        root.setCenter(imageView);
        root.setBottom(rows);
        Scene scene = new Scene(root, DEFAULT_WIDTH, DEFAULT_HEIGHT + 200);
        stage.setScene(scene);
        stage.setTitle("仿射变换演示");
        stage.setResizable(false);
        stage.show();
    }
}
